import os
import traceback
import logging

import uvicorn
import requests
import socket
from fastapi import FastAPI, HTTPException, UploadFile, File, Form, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import StreamingResponse
from pydantic import BaseModel

import redis.asyncio as redis  # 替换原来的 redis 导入

import json
import uuid
import asyncio
from typing import Generator
from starlette.requests import ClientDisconnect

from core.simple_agent import SimpleAgent
from tools.upload_manager import UploadManager

# 初始化日志记录器
logger = logging.getLogger(__name__)

# 创建 FastAPI 应用
app = FastAPI(
    title="AgenticSeek Backend",
    description="本地AI智能体系统",
    version="1.1.0"
)

# 添加 CORS 中间件
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# Redis连接 - 支持Docker和本地环境
try:
    # 从环境变量获取Redis主机，Docker环境下为redis服务名，本地为localhost
    redis_host = os.getenv('REDIS_HOST', 'redis')  # Docker环境下默认为redis服务名
    redis_port = int(os.getenv('REDIS_PORT', '6379'))
    redis_client = redis.Redis(host=redis_host, port=redis_port, db=0, decode_responses=True)
    print(f"✅ Redis配置为: {redis_host}:{redis_port}")
except Exception as e:
    print(f"❌ Redis连接配置失败: {e}")
    redis_client = None

# 创建上传管理器实例
upload_manager = UploadManager()
#
# 添加错误处理的智能体初始化
try:
    # 创建智能体实例
    agent = SimpleAgent(upload_manager=upload_manager)
    print("✅ 智能体初始化成功")
except Exception as e:
    print(f"❌ 智能体初始化失败: {e}")
    print("详细错误信息:")
    traceback.print_exc()
    agent = None


@app.on_event("startup")
async def startup_event():
    """应用启动时的初始化事件"""
    if agent is not None:
        print("🔄 开始加载已存在的文件...")
        # 异步初始化智能体组件
        await agent._initialize_components()
        
        # 加载已存在的文件
        try:
            workspace_dir = "workspace"
            if os.path.exists(workspace_dir):
                for session_dir in os.listdir(workspace_dir):
                    session_path = os.path.join(workspace_dir, session_dir)
                    if os.path.isdir(session_path):
                        print(f"🔍 检查会话目录: {session_dir}")
                        for filename in os.listdir(session_path):
                            file_path = os.path.join(session_path, filename)
                            if os.path.isfile(file_path):
                                try:
                                    with open(file_path, 'rb') as f:
                                        file_content = f.read()
                                    
                                    # 注册到上传管理器
                                    file_info = upload_manager.save_uploaded_file(
                                        session_dir, filename, file_content
                                    )
                                    print(f"✅ 加载文件: {filename} (会话: {session_dir})")
                                except Exception as e:
                                    print(f"❌ 加载文件失败 {filename}: {e}")
                
                print(f"✅ 系统启动时成功加载了已存在的文件")
        except Exception as e:
            print(f"❌ 加载已存在文件时出错: {e}")


class ChatRequest(BaseModel):
    message: str
    session_id: str = "default"


@app.get("/")
async def root():
    """根路径"""
    return {
        "message": "AgenticSeek 后端服务运行成功！",
        "status": "running",
        "version": "1.1.0",
        "features": ["基础API", "Redis缓存", "智能体对话", "文件操作", "网页浏览"]
    }


@app.get("/health")
async def health_check():
    """健康检查端点"""
    redis_status = await check_redis_status()

    llm_status = "available" if await agent.llm.is_available() else "unavailable"

    # 检查浏览器状态
    # browser_status = "available" if agent.web_browser.driver else "unavailable"
    browser_status = "available"

    return {
        "status": "healthy",
        "service": "agenticseek-backend",
        "redis": redis_status,
        "llm": llm_status,
        "browser": browser_status
    }


@app.post("/api/chat/stream")
async def chat_stream_endpoint(request: ChatRequest, http_request: Request):
    """流式聊天接口"""
    try:
        # 将会话ID添加到Redis会话集合中（如果不存在）
        await redis_client.sadd("sessions", request.session_id)
        
        # 生成用户消息的时间戳
        from datetime import datetime
        user_timestamp = datetime.utcnow().isoformat() + "Z"
        
        # 先保存用户消息到Redis
        await redis_client.rpush(f"chat:{request.session_id}", json.dumps({
            "user": request.message,
            "user_timestamp": user_timestamp,
            "type": "user"
        }))
        
        async def generate_response():
            """生成流式响应"""
            full_response = ""
            try:
                # 发送开始处理信号
                yield f"data: {json.dumps({'type': 'start'})}\n\n"
                
                # 使用智能体的流式处理方法
                async for chunk in agent.process_message_stream(request.message, request.session_id):
                    # 检查客户端是否断开连接
                    if await http_request.is_disconnected():
                        # 客户端断开连接，停止生成
                        await agent.stop_generation(request.session_id)
                        break
                    
                    full_response += chunk
                    # 发送SSE格式的数据
                    try:
                        yield f"data: {json.dumps({'chunk': chunk, 'type': 'content'})}\n\n"
                    except Exception as send_error:
                        # 如果发送失败，说明客户端已断开，停止生成
                        await agent.stop_generation(request.session_id)
                        break
                
                # 生成助手回复的时间戳（使用UTC时间并添加Z后缀）
                assistant_timestamp = datetime.utcnow().isoformat() + "Z"
                
                # 只有在没有被中断的情况下才发送完成信号
                if not await http_request.is_disconnected():
                    yield f"data: {json.dumps({'type': 'done', 'timestamp': assistant_timestamp})}\n\n"
                
                # 保存助手回复到Redis（只有在有响应内容时）
                if full_response.strip():
                    await redis_client.rpush(f"chat:{request.session_id}", json.dumps({
                        "assistant": full_response,
                        "assistant_timestamp": assistant_timestamp,
                        "type": "assistant"
                    }))
                
            except ClientDisconnect:
                # 客户端断开连接
                await agent.stop_generation(request.session_id)
            except Exception as e:
                # 发送错误信息（如果连接仍然活跃）
                try:
                    if not await http_request.is_disconnected():
                        error_msg = f"处理消息时出错: {str(e)}"
                        yield f"data: {json.dumps({'chunk': error_msg, 'type': 'error'})}\n\n"
                        yield f"data: {json.dumps({'type': 'done'})}\n\n"
                except:
                    # 如果发送错误信息也失败，说明连接已断开
                    await agent.stop_generation(request.session_id)

        return StreamingResponse(
            generate_response(),
            media_type="text/plain",
            headers={
                "Cache-Control": "no-cache",
                "Connection": "keep-alive",
                "Content-Type": "text/event-stream"
            }
        )

    except Exception as e:
        raise HTTPException(status_code=500, detail=f"处理流式消息时出错: {str(e)}")


@app.post("/api/chat/stop/{session_id}")
async def stop_chat_endpoint(session_id: str):
    """停止指定会话的聊天生成并清理当前对话轮次"""
    try:
        # 停止生成
        success = await agent.stop_generation(session_id)
        
        # 清理Redis中的最后一轮对话（用户问题 + 助手回复）
        redis_history = await redis_client.lrange(f"chat:{session_id}", 0, -1)
        messages_to_remove = 0
        
        if redis_history:
            print(f"[DEBUG] Redis历史记录数量: {len(redis_history)}")
            
            # 从后往前检查，删除最后的助手消息和用户消息
            # 检查最后一条消息
            if redis_history:
                try:
                    last_message = json.loads(redis_history[-1])
                    print(f"[DEBUG] 最后一条消息类型: {last_message.get('type')}")
                    if last_message.get("type") == "assistant":
                        messages_to_remove += 1
                        print(f"[DEBUG] 标记删除助手消息")
                except json.JSONDecodeError as e:
                    print(f"[DEBUG] 解析最后一条消息失败: {e}")
            
            # 检查倒数第二条消息是否是用户消息
            if len(redis_history) >= 2:
                try:
                    second_last_message = json.loads(redis_history[-2])
                    print(f"[DEBUG] 倒数第二条消息类型: {second_last_message.get('type')}")
                    if second_last_message.get("type") == "user":
                        messages_to_remove += 1
                        print(f"[DEBUG] 标记删除用户消息")
                except json.JSONDecodeError as e:
                    print(f"[DEBUG] 解析倒数第二条消息失败: {e}")
            elif len(redis_history) == 1:
                # 如果只有一条消息且是用户消息，也要删除
                try:
                    last_message = json.loads(redis_history[-1])
                    if last_message.get("type") == "user":
                        messages_to_remove += 1
                        print(f"[DEBUG] 标记删除单独的用户消息")
                except json.JSONDecodeError as e:
                    print(f"[DEBUG] 解析单条消息失败: {e}")
            
            # 如果没有找到要删除的消息，但是有Redis历史，可能是格式问题
            # 尝试删除最后一条消息（可能是未完成的用户消息）
            if messages_to_remove == 0 and redis_history:
                print(f"[DEBUG] 未找到标准格式消息，尝试删除最后一条消息")
                messages_to_remove = 1
            
            print(f"[DEBUG] 计划删除消息数量: {messages_to_remove}")
            
            # 删除相应数量的消息
            for i in range(messages_to_remove):
                try:
                    deleted_msg = await redis_client.rpop(f"chat:{session_id}")
                    if deleted_msg:
                        print(f"[DEBUG] 已删除消息 {i+1}: {deleted_msg[:100]}...")
                    else:
                        print(f"[DEBUG] 删除消息 {i+1} 失败: 没有更多消息")
                        break
                except Exception as e:
                    print(f"[DEBUG] 删除消息 {i+1} 时出错: {e}")
                    break
            
            print(f"🧹 已从Redis中删除 {messages_to_remove} 条消息 (会话: {session_id})")
        else:
            print(f"[DEBUG] Redis中没有找到会话历史: {session_id}")
        
        # 清理智能体中的最后一轮对话
        agent.clear_last_conversation_round(session_id)
        
        return {
            "status": "success" if success else "partial_success",
            "session_id": session_id,
            "message": f"会话 {session_id} 的生成已停止，当前对话轮次已清理"
        }
    except Exception as e:
        print(f"❌ 停止聊天失败: {str(e)}")
        raise HTTPException(status_code=500, detail=f"停止聊天失败: {str(e)}")


@app.post("/api/chat/stop_all")
async def stop_all_chat_endpoint():
    """停止所有活跃的聊天生成"""
    try:
        success = await agent.stop_all_generations()
        return {
            "status": "success" if success else "failed",
            "message": "所有生成已停止" if success else "停止失败"
        }
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"停止所有生成时出错: {str(e)}")





@app.get("/api/sessions/{session_id}")
async def get_chat_history(session_id: str):
    """获取聊天历史"""
    try:
        # 从Redis获取历史
        redis_history = await redis_client.lrange(f"chat:{session_id}", 0, -1)
        redis_messages = [json.loads(item) for item in redis_history]

        # 从智能体获取当前会话历史
        agent_history = agent.get_session_history(session_id)

        return {
            "session_id": session_id,
            "redis_history": redis_messages,
            "agent_history": agent_history
        }
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"获取历史时出错: {str(e)}")


async def check_redis_status():
    """检查 Redis 连接状态"""
    try:
        await redis_client.ping()
        return "connected"
    except Exception as e:
        print(f"Redis 连接失败: {e}")
        return "disconnected"


@app.get("/api/agent/status")
async def agent_status():
    """智能体状态检查"""
    redis_status = await check_redis_status()
    
    # 安全检查LLM可用性
    llm_available = False
    if agent.llm is not None:
        try:
            llm_available = await agent.llm.is_available()
        except Exception as e:
            logger.error(f"检查LLM状态时出错: {e}")
            llm_available = False

    return {
        "agent": "SimpleAgent",
        "llm_available": llm_available,
        # "browser_available": agent.web_browser.driver is not None,
        "browser_available": "available",
        "redis": redis_status,
        "features": ["file_operations", "web_browsing", "basic_chat"]
    }


class UpdateSessionRequest(BaseModel):
    name: str


@app.put("/api/sessions/{session_id}")
async def update_session(session_id: str, request: UpdateSessionRequest):
    """更新会话名称"""
    try:
        # 检查会话是否存在
        session_exists = await redis_client.sismember("sessions", session_id)
        if not session_exists:
            raise HTTPException(status_code=404, detail="会话不存在")
        
        # 获取会话的第一条消息
        history = await redis_client.lrange(f"chat:{session_id}", 0, 0)
        if not history:
            raise HTTPException(status_code=404, detail="会话历史不存在")
        
        # 更新第一条消息中的用户内容为新名称
        first_msg = json.loads(history[0])
        first_msg["user"] = request.name
        
        # 更新Redis中的第一条消息
        await redis_client.lset(f"chat:{session_id}", 0, json.dumps(first_msg))
        
        return {"status": "success", "message": f"会话名称已更新为: {request.name}"}
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"更新会话名称失败: {str(e)}")


@app.delete("/api/sessions/{session_id}")
async def clear_session(session_id: str):
    """清除会话历史和相关数据"""
    try:
        # 清除智能体会话
        agent.clear_session(session_id)
        
        # 清除Redis中的聊天记录
        await redis_client.delete(f"chat:{session_id}")
        
        # 从会话列表中移除
        await redis_client.srem("sessions", session_id)
        
        # 清除会话相关的上传文件
        upload_manager.clear_session_files(session_id)
        
        return {"status": "success", "message": f"会话 {session_id} 及其相关数据已完全清除"}
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"清除会话失败: {str(e)}")


@app.get("/api/sessions")
async def get_sessions():
    """获取所有会话列表"""
    try:
        # 从Redis获取会话列表
        sessions = await redis_client.smembers("sessions")
        session_list = []
        
        for session_id in sessions:
            # 获取每个会话的基本信息
            history = await redis_client.lrange(f"chat:{session_id}", 0, 0)  # 获取第一条消息
            created_time = None
            first_message = "新会话"
            
            if history:
                try:
                    first_msg = json.loads(history[0])
                    created_time = first_msg.get("timestamp")
                    first_message = first_msg.get("user", "新会话")[:30] + ("..." if len(first_msg.get("user", "")) > 30 else "")
                except:
                    pass
            
            session_list.append({
                "id": session_id,
                "name": first_message,
                "created_time": created_time or "未知时间"
            })
        
        # 按创建时间排序（最新的在前）
        session_list.sort(key=lambda x: x["created_time"], reverse=True)
        
        return {
            "sessions": session_list,
            "count": len(session_list)
        }
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"获取会话列表失败: {str(e)}")


@app.post("/api/sessions")
async def create_session():
    """创建新会话"""
    try:
        from datetime import datetime
        
        # 生成新的会话ID
        session_id = f"session-{uuid.uuid4().hex[:8]}"
        
        # 将会话ID添加到Redis会话集合中
        await redis_client.sadd("sessions", session_id)
        
        # 创建初始时间戳记录
        current_timestamp = datetime.utcnow().isoformat() + "Z"
        await redis_client.rpush(f"chat:{session_id}", json.dumps({
            "user": "会话创建",
            "assistant": "新会话已创建",
            "timestamp": current_timestamp
        }))
        
        return {
            "status": "success",
            "session_id": session_id,
            "message": "新会话创建成功"
        }
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"创建会话失败: {str(e)}")


@app.get("/api/network/test")
async def network_test():
    """网络连接测试 - 使用国内可访问的网站"""
    test_urls = [
        "https://www.baidu.com",
        "https://www.bing.com"
    ]

    results = {}

    for url in test_urls:
        try:
            response = requests.get(url, timeout=10)
            results[url] = {
                "status": "success",
                "status_code": response.status_code
            }
        except Exception as e:
            results[url] = {
                "status": "failed",
                "error": str(e)
            }

    # 测试DNS解析
    try:
        socket.gethostbyname("www.baidu.com")
        dns_status = "success"
    except Exception as e:
        dns_status = f"failed: {str(e)}"

    return {
        "network_test": results,
        "dns_resolution": dns_status,
        "container_hostname": socket.gethostname(),
        "note": "测试使用国内可访问的网站"
    }


@app.post("/api/upload")
async def upload_file(
        file: UploadFile = File(...),
        session_id: str = Form("default")
):
    """文件上传端点"""
    try:
        content = await file.read()
        file_info = upload_manager.save_uploaded_file(session_id, file.filename, content)

        return {
            "status": "success",
            "message": f"文件上传成功: {file.filename}",
            "file_info": {
                "id": file_info["id"],
                "original_name": file_info["original_name"],
                "size": file_info["size"],
                "used": file_info["used"]  # 默认False，需要手动标记使用
            }
        }

    except Exception as e:
        raise HTTPException(status_code=500, detail=f"文件上传失败: {str(e)}")


@app.post("/api/upload/use/{session_id}/{file_id}")
async def mark_file_for_use(session_id: str, file_id: str):
    """标记文件为使用中"""
    try:
        success = upload_manager.mark_file_for_use(session_id, file_id)
        if success:
            file_info = upload_manager.get_file_info(session_id, file_id)
            return {
                "status": "success",
                "message": f"文件已标记为使用中: {file_info['original_name']}"
            }
        else:
            raise HTTPException(status_code=404, detail="文件不存在")
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"标记文件失败: {str(e)}")


@app.get("/api/upload/using/{session_id}")
async def get_using_files(session_id: str):
    """获取使用中的文件"""
    try:
        using_files = upload_manager.get_files_to_use(session_id)
        return {
            "session_id": session_id,
            "using_files": using_files,
            "count": len(using_files)
        }
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"获取使用中文件失败: {str(e)}")


@app.get("/api/upload/files/{session_id}")
async def get_session_files(session_id: str):
    """获取会话的所有上传文件"""
    try:
        files = upload_manager.get_session_files(session_id)
        return {
            "session_id": session_id,
            "files": files,
            "count": len(files)
        }
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"获取文件列表失败: {str(e)}")


@app.delete("/api/upload/use/{session_id}/{file_id}")
async def unmark_file_for_use(session_id: str, file_id: str):
    """取消文件的使用标记"""
    try:
        success = upload_manager.unmark_file_for_use(session_id, file_id)
        if success:
            file_info = upload_manager.get_file_info(session_id, file_id)
            return {
                "status": "success",
                "message": f"已取消文件的使用标记: {file_info['original_name']}"
            }
        else:
            raise HTTPException(status_code=404, detail="文件不存在")
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"取消文件标记失败: {str(e)}")


@app.delete("/api/upload/files/{session_id}/{file_id}")
async def delete_file(session_id: str, file_id: str):
    """删除上传的文件"""
    try:
        file_info = upload_manager.get_file_info(session_id, file_id)
        if not file_info:
            raise HTTPException(status_code=404, detail="文件不存在")
        
        success = upload_manager.delete_file(session_id, file_id)
        if success:
            return {
                "status": "success",
                "message": f"文件已删除: {file_info['original_name']}"
            }
        else:
            raise HTTPException(status_code=500, detail="删除文件失败")
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"删除文件时出错: {str(e)}")


@app.delete("/api/upload/files/{session_id}")
async def clear_session_files(session_id: str):
    """清空会话的所有上传文件"""
    try:
        success = upload_manager.clear_session_files(session_id)
        if success:
            return {
                "status": "success",
                "message": "所有文件已清空"
            }
        else:
            raise HTTPException(status_code=500, detail="清空文件失败")
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"清空文件时出错: {str(e)}")


@app.post("/api/upload/batch-use/{session_id}")
async def batch_mark_files(session_id: str, request: dict):
    """批量标记文件为使用中"""
    try:
        file_ids = request.get("file_ids", [])
        results = []
        for file_id in file_ids:
            success = upload_manager.mark_file_for_use(session_id, file_id)
            file_info = upload_manager.get_file_info(session_id, file_id)
            results.append({
                "file_id": file_id,
                "success": success,
                "file_name": file_info['original_name'] if file_info else "未知文件"
            })
        
        success_count = sum(1 for r in results if r['success'])
        return {
            "status": "success",
            "message": f"成功标记 {success_count}/{len(file_ids)} 个文件",
            "results": results
        }
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"批量标记文件失败: {str(e)}")


@app.delete("/api/upload/batch-use/{session_id}")
async def batch_unmark_files(session_id: str, request: dict):
    """批量取消文件的使用标记"""
    try:
        file_ids = request.get("file_ids", [])
        results = []
        for file_id in file_ids:
            success = upload_manager.unmark_file_for_use(session_id, file_id)
            file_info = upload_manager.get_file_info(session_id, file_id)
            results.append({
                "file_id": file_id,
                "success": success,
                "file_name": file_info['original_name'] if file_info else "未知文件"
            })
        
        success_count = sum(1 for r in results if r['success'])
        return {
            "status": "success",
            "message": f"成功取消标记 {success_count}/{len(file_ids)} 个文件",
            "results": results
        }
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"批量取消标记失败: {str(e)}")


@app.get("/api/upload/info/{session_id}/{file_id}")
async def get_file_info(session_id: str, file_id: str):
    """获取文件详细信息"""
    try:
        file_info = upload_manager.get_file_info(session_id, file_id)
        if not file_info:
            raise HTTPException(status_code=404, detail="文件不存在")
        
        # 获取文件解析信息
        parse_info = upload_manager.get_file_parse_info(session_id, file_id)
        
        return {
            "file_info": file_info,
            "parse_info": parse_info
        }
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"获取文件信息失败: {str(e)}")


if __name__ == "__main__":
    import sys
    
    # 从环境变量获取端口，Docker环境下默认为8000
    port = int(os.getenv('PORT', '8000'))
    
    # 检查命令行参数中是否有端口设置
    if len(sys.argv) > 1:
        for i, arg in enumerate(sys.argv):
            if arg == "--port" and i + 1 < len(sys.argv):
                try:
                    port = int(sys.argv[i + 1])
                except ValueError:
                    print("❌ 端口参数无效，使用默认端口")
    
    # 检查是否在Docker环境中
    is_docker = os.getenv('ENVIRONMENT') == 'development' or os.path.exists('/.dockerenv')
    reload_mode = not is_docker  # Docker环境下不使用reload模式
    
    print("🚀 启动 AgenticSeek 服务...")
    print(f"🐳 运行环境: {'Docker' if is_docker else '本地'}")
    print(f"📡 访问地址: http://0.0.0.0:{port}")
    if not is_docker:
        print(f"📡 本地访问: http://localhost:{port}")
    
    uvicorn.run(
        "main:app",
        host="0.0.0.0",
        port=port,
        reload=reload_mode,
        log_level="info",
        timeout_keep_alive=300,  # 增加keep-alive超时到5分钟
        timeout_graceful_shutdown=300  # 增加优雅关闭超时到5分钟
    )
